<template>
  <div class="rank-box">
    <div>
      <el-button class="refresh" size="small" type="primary" @click="start"
        >刷新</el-button
      >
    </div>
    <!-- :max-height="`calc(100vh - 64px - var(--container-margin)* 5 - var(--container-header-height) - var(--rank-header-height )`" -->
    <el-table
      size="small"
      class="match-rank-table"
      :data="tableData"
      style="width: 100%"
      :max-height="`calc(100vh - 24px)`"
    >
      <template v-slot:empty>
        <el-empty
          image="https://cdn-hterobot.oss-cn-qingdao.aliyuncs.com/image/ojSass-empty.png"
        />
      </template>
      <el-table-column
        type="index"
        label="排名"
        align="center"
        width="50"
      ></el-table-column>
      <el-table-column
        prop="username"
        label="成员"
        align="center"
        width="120"
        show-overflow-tooltip
      >
        <template #default="{ row }">
          {{ row.xingming ? `${row.xingming} (${row.name})` : row.name }}
        </template>
      </el-table-column>
      <el-table-column
        prop="df"
        label="总分"
        align="center"
        width=""
        sortable
      ></el-table-column>

      <el-table-column
        v-for="(item, key) in matchQuestion"
        :key="key"
        align="center"
        :prop="String(key)"
        :label="`#${key + 1}(${item.score})`"
        class-name="el-table-column"
        label-class-name="match-rank-table-header"
        width=""
        sortable
      >
        <template #header="{ row }">
          <el-tooltip
            effect="dark"
            :content="`${item.type}-${item.name}`"
            placement="top"
          >
            <div>
              <div class="match-rank-table-header-qNo">
                {{ `# ${key + 1} ` }}
              </div>
              <div class="match-rank-table-header-qScore">
                {{ `${item.score} ` }}
              </div>
            </div>
          </el-tooltip>
        </template>
        <template #default="{ row }">
          <!-- 代码题-弹窗展示源码和测试点情况 只有isAdmin或者本人 才能查看代码、测试点信息 -->
          <div v-if="item.type === '代码'">
            <el-tooltip
              v-if="iswatchcode || isAdmin || row.userId === user?.ID"
              effect="dark"
              :content="`点击查看详情`"
              placement="top"
            >
              <div
                :class="className(row, item, key)"
                class="cursor-pointer score-box"
                @click="showCode(row, item, key)"
              >
                {{ row.rank[key].score }}
              </div>
            </el-tooltip>
            <div v-else :class="className(row, item, key)" class="score-box">
              {{ row.rank[key].score }}
            </div>
          </div>
          <div v-else :class="className(row, item, key)">
            {{ row.rank[key].score }}
          </div>
        </template>
      </el-table-column>
    </el-table>
    <!-- 弹窗 -->
    <el-dialog
      class="dialog"
      v-model="dialogVisible"
      width="96%"
      :before-close="handleClose"
    >
      <template #header>
        <span class="icon-text el-dialog__header-title">
          <el-icon class="text-primary">
            <Guide />
          </el-icon>
          得分详情
        </span>
      </template>
      <div class="">
        <h6 class="source-detail-title">
          {{ sourceDetail.userName }} - {{ sourceDetail.name }}
        </h6>
        <h6 class="margin-bottom0">题目总得分：{{ sourceDetail.score }}</h6>
      </div>
      <el-row class="dialog-content">
        <el-col :span="8" class="left">
          <el-row class="test-case-title">
            <h6 class="margin-bottom0">测试点得分</h6>
          </el-row>
          <el-scrollbar>
            <div
              v-if="Array.isArray(sourceDetail.info)"
              v-for="(info1, infok) in subtask(sourceDetail.info)"
              :key="'a_' + infok"
            >
              <div class="col-12">Subtask {{ infok + 1 }}</div>
              <div class="col-12">
                <div
                  class="test-case-container"
                  v-for="(e, k) in info1"
                  :key="`b_${k}`"
                  :class="e.tn ? e.tn[1] : ALLTYPE['RE'][1]"
                >
                  <div class="test-case-content">
                    <div class="test-case-head">
                      <span class="test-case-num font-size14"
                        >#{{ k + 1 }}</span
                      >
                      <span
                        class="test-case-ac"
                        v-text="e.tn ? e.tn[0] : 'RE'"
                      ></span>
                    </div>
                    <strong class="test-case-score">{{ e.score || 0 }}</strong>
                    <span class="test-case-time font-size13">
                      {{ time(sourceDetail.result, [e]) }}/{{
                        memory(sourceDetail.result, e)
                      }}
                    </span>
                  </div>
                </div>
              </div>
            </div>
          </el-scrollbar>
        </el-col>
        <el-col :span="16" class="right">
          <el-row class="source-code-title">
            <h6 class="margin-bottom0">源代码</h6>
            <div class="margin-left10">
              <!-- <el-button class="submission-btn" size="small" type="primary"
                                @click="myCopy(sourceDetail.sourceCode || '')">复制源代码
                            </el-button> -->
              <el-icon
                class="icon cursor-pointer"
                @click="myCopy(sourceDetail.sourceCode || '')"
              >
                <CopyDocument />
              </el-icon>
            </div>
          </el-row>
          <div class="col p-0">
            <pre v-highlight :class="'language-' + sourceDetail.sourceLanguage">
                    <code v-text="sourceDetail.sourceCode" class="code-pre"></code>
                </pre>
          </div>
        </el-col>
      </el-row>
      <template #footer>
        <el-button type="primary" size="small" @click="dialogVisible = false"
          >关闭</el-button
        >
      </template>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { useClipboard } from "@vueuse/core";
import { ElMessage } from "element-plus";
import { encode } from "js-base64";
import { computed, onMounted, reactive, ref, watch } from "vue";
import { useRouter } from "vue-router";
import { useStore } from "vuex";
import {
  asyncgetactivity,
  asyncselectmatchquestion,
  asyncteamselectmatchuserquestion,
} from "../../../api";
import { Storekey } from "../../../interface";
import Studycodemodel from "../../../mysql_interface/studycodemodel";
import { ALLTYPE } from "@/util/jianchatime";
import subtask from "../../../util/subtask";
const { text, copy, copied, isSupported } = useClipboard();

type P = {
  activity: Studycodemodel.Iactivity;
};
const p = defineProps<P>();
const router = useRouter();
const store = useStore(Storekey);
const user = computed(() => {
  return store.state.user;
});
const teamId = computed(() => {
  return user.value?.teamid;
});
const isAdmin = computed(() => {
  return user.value?.isAdmin || user.value?.remarks == "销售助教";
});

const limit = ref(1000);
const offset = ref(0);
const activity = reactive<Studycodemodel.Iactivity>({});
const format = computed(() => {
  return p.activity.format;
});
const iswatchcode = computed(() => {
  return activity.iswatchcode;
});

const matchQuestion = ref<Studycodemodel.Imatchquestion[]>([{ score: 0 }]);
async function selectmatchquestion() {
  const [data] = await asyncselectmatchquestion({ activityid: p.activity.ID });
  matchQuestion.value = data.matchquestion || [];
}

watch(
  () => p.activity.ID,
  (v) => {
    if (v) start();
  }
);
onMounted(async () => {
  await start();
});
async function start() {
  if (!p.activity.ID) return;
  const data = await asyncgetactivity({ ID: p.activity.ID });
  Object.assign(activity, data);
  if (!activity.isranking) return;
  await selectmatchquestion();
  getMatchRanking();
}

const users = ref<Studycodemodel.Iuser[]>([]);
const matchRanking = ref<Studycodemodel.Imatchranking[]>([]);
// 比赛成绩
async function getMatchRanking() {
  // 获取团队成员、管理员显示比赛记录
  const teamInfo = await asyncgetteam({ ID: activity.teamid });
  const isadminshowactivity = teamInfo?.isadminshowactivity;
  // 团队所有管理
  const teamAdminUsers = teamInfo?.teamuser || [];

  // 1. 获取数据
  const { rows } = await asyncteamselectmatchuserquestion({
    activityid: p.activity.ID,
    teamid: activity.teamid || teamId.value,
    _offset: offset.value,
    _limit: limit.value,
  });
  // 2. 预处理题目数据
  const questions = matchQuestion.value;
  const questionIndexMap = new Map(questions.map((q, index) => [q.ID, index]));
  // 3. 处理用户数据
  const result = new Map();
  for (const row of rows) {
    const {
      userid,
      user,
      questionid,
      df = 0,
      type = "代码",
      resultdata = "UN",
      info,
      activityid,
      createdAt,
      updatedAt,
    } = row;
    // 3.1 跳过不在题目列表的 questionid
    const index = questionIndexMap.get(questionid);
    if (index === undefined) continue;
    // 3.2 初始化用户数据
    if (!result.has(userid)) {
      result.set(userid, {
        user: {
          ID: userid,
          name: user?.name || "",
          xingming: user?.xingming || "",
        },
        totalScore: 0,
        firstRow: row,
        questionData: new Array(questions.length).fill(null),
      });
    }
    // 3.3 更新用户分数 & 题目信息
    const userData = result.get(userid);
    userData.totalScore += df;
    userData.questionData[index] = [
      type,
      df,
      questionid,
      resultdata,
      Array.isArray(info)
        ? info.map(({ score = 0, t = 0, memory = 0, tn = [] }) => [
            score,
            t,
            memory,
            tn[0] || "UN",
          ])
        : [],
    ];
  }
  // 4. 生成最终排名数据
  matchRanking.value = Array.from(result.values()).map((user) => {
    const filledData = user.questionData.map(
      (data, i) => data || ["代码", 0, questions[i].ID, "UN", []]
    );
    return {
      userid: user.user.ID,
      activityid: user.firstRow?.activityid,
      df: user.totalScore,
      username: user.user.name,
      starttime: user.firstRow?.createdAt,
      endtime: user.firstRow?.createdAt,
      createdAt: user.firstRow?.createdAt,
      updatedAt: user.firstRow?.updatedAt,
      data: JSON.stringify(filledData), // 可考虑延迟序列化
    };
  });
  // 5. 更新用户列表 增加判断：管理员是否显示成绩
  users.value = Array.from(result.values()).reduce((acc, u) => {
    if (
      isadminshowactivity ||
      !teamAdminUsers.some((admin) => admin.userid === u.user.ID)
    ) {
      acc.push(u.user);
    }
    return acc;
  }, []);
}

const tableData = computed(() => {
  const rankingById = Object.fromEntries(
    matchRanking.value.map((r) => [r.userid, r])
  );

  return users.value
    .map((u) => {
      const matchranking = rankingById[u.ID] || {};
      const _data: any[] = JSON.parse(matchranking.data || "[]");

      // 创建题目索引映射
      const questionIndexMap = new Map<number, number>();
      _data.forEach((item, index) => {
        if (item && item[2] !== undefined) {
          questionIndexMap.set(item[2], index);
        }
      });

      const rank = matchQuestion.value.map((question) => {
        const index = questionIndexMap.get(question.ID);
        const answered = index !== undefined ? _data[index] : null;
        return {
          type: question.type,
          score: answered ? answered[1] || 0 : 0,
          acList: answered ? answered[3] || [] : [],
          isAnswered: !!answered,
        };
      });

      return {
        rank,
        ...u,
        ...matchranking,
        resultdata:
          _data.length > 0 && _data.every((e) => e[3] === "AC") ? "AC" : "UN",
      };
    })
    .sort((a, b) => {
      const dfDiff = Number(b.df) - Number(a.df);
      return dfDiff;
      if (dfDiff !== 0) {
        return dfDiff;
      }
      return a.xingming?.localeCompare(b.xingming, "zh");
    });
});

//
const dialogVisible = ref(false);
const sourceDetail = reactive({
  userName: "",
  name: "",
  score: "",
  testScores: [],
  sourceCode: "",
  sourceLanguage: "",
  //
  info: [],
  result: "",
});

async function showCode(
  userMatchQuestion: any,
  item: Studycodemodel.Imatchquestion,
  index: number
) {
  const { userid, name, score } = userMatchQuestion;
  sourceDetail.userName = name || "";
  sourceDetail.name = item.name || "";
  if (item.type === "代码") {
    try {
      const { rows } = await asyncteamselectmatchuserquestion({
        userid,
        questionid: item.ID,
        activityid: p.activity.ID,
        teamid: activity.teamid || teamId.value,
      });
      if (rows.length) {
        const { context, language, info } = rows[0];
        sourceDetail.sourceLanguage = language || "";
        sourceDetail.sourceCode = context || "";
        // 使用 selectmatchuserquestion 返回的info 重新计算subtask 内部测试点
        (sourceDetail.info as
          | Studycodemodel.Iuserquestioninfo
          | Studycodemodel.Iuserquestioninfo[]
          | undefined) = info;
        sourceDetail.testScores = userMatchQuestion.rank[index]?.acList;
        sourceDetail.score = userMatchQuestion.rank[index]?.score;
        dialogVisible.value = true;
      } else {
        ElMessage.error("没有找到此题答题记录");
      }
    } catch (e) {
      ElMessage.error(e as any);
    }
  } else {
    ElMessage.error("此题不是代码题");
  }
}

function handleClose(done: () => void) {
  dialogVisible.value = false;
  done();
}

async function getcode(params: any, item: Studycodemodel.Imatchquestion) {
  const { userid, name } = params;
  const questionid = item.ID;
  const activityid = p.activity.ID;
  const teamid = activity.teamid || teamId.value;
  if (item.type === "代码") {
    try {
      const { rows } = await asyncteamselectmatchuserquestion({
        userid,
        questionid,
        activityid,
        teamid,
      });
      if (rows.length) {
        const { context, language } = rows[0];
        window.open(
          `https://cdn.hterobot.com/diffeditor/index.html?code1=${encode(
            String(context),
            true
          )}&language=${language}&name1=${encode(name + " " + item.name, true)}`
        );
      } else {
        ElMessage.error("没有找到此题");
      }
    } catch (e) {
      ElMessage.error(e as any);
    }
  } else {
    ElMessage.error("此题不是代码题");
  }
}
function className(
  params: Studycodemodel.Imatchranking,
  matchquestion: Studycodemodel.Imatchquestion,
  index: number
) {
  const { data } = params;
  const _data = JSON.parse(data || "[]") as any[];
  const n = _data[index] || [];
  const c = (n[1] || 0) / Number(matchquestion.score);
  if (c >= 0.7) {
    return "Accepted";
  } else if (c >= 0.3) {
    return "Unaccepted";
  } else {
    return "CompileError";
  }
}
function myCopy(str: string) {
  try {
    str ? copy(str) : "";
    ElMessage.success("复制成功");
  } catch (error) {
    console.error("myCopy error=>", str, error);
  }
}

function time(
  esult: Studycodemodel.Iuserquestion["result"],
  info: Studycodemodel.Iuserquestion["info"]
) {
  if (!Array.isArray(info)) return NaN;
  let t = 0;
  for (let v of info) {
    if (t < Number(v.t)) {
      t = Number(v.t);
    }
  }
  return Math.floor(t) + "ms";
}
function memory(
  esult: Studycodemodel.Iuserquestion["result"],
  info: Studycodemodel.Iuserquestioninfo
) {
  if (typeof info.memory === "number") {
    return info.memory / 1024 < 1
      ? Math.floor(info.memory) + "kb"
      : Math.floor(info.memory / 1024) + "mb";
  } else if (typeof info.memory === "object") {
    return "4kb";
  }
  return info.memory || "4kb";
}
</script>

<style scoped>
.rank-box {
  /* height: calc(100vh - 64px - var(--container-margin)* 5 - var(--container-header-height)); */
  margin: 0;
  height: 100%;
  /* 设置为可以滚动 */
  overflow-y: auto;
  --rank-header-height: 40px;
}

.fixed-header .el-table__header-wrapper {
  position: sticky;
  top: 0;
  /* 固定在顶部 */
  z-index: 10;
  /* 确保表头在内容之上 */
  background-color: #fff;
  /* 设置表头背景色，避免遮挡问题 */
}

.rank-header {
  display: flex;
  justify-content: space-between;
  height: var(--rank-header-height);
  align-items: center;
}

.refresh {
  border-radius: 5px;
}

.cu:hover {
  text-decoration: underline;
}

.match-rank-table-header-qNo,
.match-rank-table-header-qScore {
  position: relative;
}

.match-rank-table-header-qScore {
  color: #7f7f7f;
  font-size: 12px;
}

.tips {
  position: absolute;
  left: 0;
  /* display: inline-block; */
  /* float: left; */
}

.score-box {
  border: 1px solid;
  max-width: 90px;
  margin: auto;
  padding: 5px 0;
  opacity: 0.5;
  color: #fff;
}

.score-box:hover {
  opacity: 1;
}

/*  */
.dialog-content {
}

.left {
  /* padding-left: 0; */
}

.right {
  /* padding-right: 0; */
}

.source-detail-title {
  margin-bottom: 12px;
}

.source-code-title,
.test-case-title {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin: 15px 0;
}

.source-code-title {
  justify-content: flex-start;
}

/* 测试点 */

.test-case-container {
  width: 108px;
  height: 104px;
  /* padding: 5px; */
  margin: 5px;
  display: inline-block;
  border-radius: 5px;
  overflow: hidden;
  box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.1);
}

.test-case-container .test-case-content {
  border-top-width: 4px;
  border-top-style: solid;
}

.test-case-content .test-case-num {
  padding-left: 8px;
  padding-right: 4px;
  color: #909399 !important;
}

.test-case-content .test-case-score {
  font-size: 36px;
  display: block;
  text-align: center;
}

.test-case-content .test-case-time {
  display: block;
  text-align: center;
  color: #909399 !important;
}
</style>

<style>
#activity-common-layout .match-rank-table-header .cell .caret-wrapper {
  /* width: 10px; */
}

#activity-common-layout .match-rank-table-header .cell {
  padding: 0;
  margin: auto;
  /* 换行 */
  white-space: pre-wrap;
  line-height: normal;
  /* 排序 */
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

#activity-common-layout .score-box.Accepted {
  background-color: var(--color-3ac2a2);
}

#activity-common-layout .score-box.Unaccepted {
  background-color: #f5ac50;
}

#activity-common-layout .score-box.CompileError {
  background-color: #ff5249;
}

#activity-common-layout .dialog .el-dialog__body {
  padding: 10px;
  height: auto;
}
</style>
